package view;

import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import java.awt.BorderLayout;
import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JToolTip;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
import javax.swing.plaf.basic.BasicComboBoxRenderer;

import com.swtdesigner.SwingResourceManager;

import model.Ant;
import model.Cell;
import model.Point;
import model.Resource;
import model.Space2D;

import util.SpaceUtilities;


public class SpaceView extends JFrame implements Runnable {
	private JTextField antDelayTextField;
	private JTextField antYTextField;
	private JTextField antXTextField;
	private JComboBox unitLenComboBox;
	private JComboBox orientationComboBox;
	private JTextField resNb3textField;
	private JTextField resNb2textField;
	private JTextField resNb1textField;
	private static final long serialVersionUID = 4383071236010601424L;
	private ButtonGroup gridSizeButtonGroup = new ButtonGroup();
	private JComboBox actionComboBox;
	
	private final JCanvasPanel canvasPanel;
	private int delay;
	private boolean stopSignal;
	private Cursor crossCursor;
	private Cursor defaultCursor;
	private Cursor handCursor;
	
	//thread for the animation
	private Thread runner;
	
	/**
	 * Launch the application
	 * @param args
	 */
	public static void main(String args[]) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					SpaceView frame = new SpaceView();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}
	
	/**
	 * Create the frame
	 */
	public SpaceView() {
		super();
		
		delay = 50;
		stopSignal = true;
		crossCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
		defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
		handCursor = new Cursor(Cursor.HAND_CURSOR);
		
		setIconImage(SwingResourceManager.getImage(SpaceView.class, "iconAnt.png"));
		setResizable(true);
		setTitle("IAI -- Ant Land");
		setBounds(100, 0, 620, 700);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		canvasPanel = new JCanvasPanel();
		//final JScrollPane canvasPanel = new JScrollPane();
		//canvasPanel.setPreferredSize(new Dimension(500, 500));
		canvasPanel.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		canvasPanel.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
		canvasPanel.setAutoscrolls(true);
		//canvasPanel.setLayout(new ScrollPaneLayout());
		canvasPanel.setBorder(new LineBorder(Color.black, 1, false));
		getContentPane().add(canvasPanel);

		//the declarations moved up here
		final JCheckBox ajustAttrAreaCheckBox = new JCheckBox();
		final JCheckBox attrZoneCheckBox = new JCheckBox();
		
		final JGridPanel gridPanel = new JGridPanel();
		gridPanel.setLayout(null);
		gridPanel.setToolTipText("");
		//add listener for the grid panel
		gridPanel.addMouseListener(new MouseAdapter() {
			public void mouseEntered(final MouseEvent arg0) {
				if(ajustAttrAreaCheckBox.isSelected()) {
					gridPanel.setCursor(handCursor);
				} else {
					gridPanel.setCursor(crossCursor);
				}
			}
			public void mouseExited(final MouseEvent e) {
				gridPanel.setCursor(defaultCursor);
			}
			//when click event occur, add the current point into attraction point set
			public void mouseClicked(final MouseEvent arg0) {
				//TODO add current point into the attraction zone
				if(attrZoneCheckBox.isSelected() && ajustAttrAreaCheckBox.isSelected()) {
					Point p = SpaceUtilities.transformCoordinate(arg0.getPoint(), gridPanel.getUnitLen());
					gridPanel.addAttractionPoint(p);
					//System.out.println("Set the point " + p.toString() + " as attraction point.");
					gridPanel.repaint();
				}
			}
		});
		canvasPanel.getViewport().setView(gridPanel);
		canvasPanel.setGridPanel(gridPanel);

		final JTabbedPane tabbedPane = new JTabbedPane();
		tabbedPane.setBorder(new LineBorder(Color.black, 1, false));
		getContentPane().add(tabbedPane, BorderLayout.NORTH);

		final JPanel tab0 = new JPanel();
		tab0.setPreferredSize(new Dimension(0, 0));
		tab0.setLayout(null);
		tabbedPane.addTab("Space Dimension", null, tab0, "Appling the setting in this tab will erase current grid maps in the canvas.");

		final JLabel gridSizeLabel = new JLabel();
		gridSizeLabel.setBounds(0, 4, 70, 20);
		gridSizeLabel.setText("Grid size :");
		tab0.add(gridSizeLabel);

		final JRadioButton bigRadioButton = new JRadioButton();
		bigRadioButton.setBounds(160, 4, 100, 20);
		gridSizeButtonGroup.add(bigRadioButton);
		bigRadioButton.setText("500 x 500");
		tab0.add(bigRadioButton);

		final JRadioButton smallRadioButton = new JRadioButton();
		smallRadioButton.setBounds(70, 4, 80, 20);
		smallRadioButton.setSelected(true);
		gridSizeButtonGroup.add(smallRadioButton);
		smallRadioButton.setText("50 x 50");
		tab0.add(smallRadioButton);

		final JLabel unitLengthLabel = new JLabel();
		unitLengthLabel.setText("Unit Length :");
		unitLengthLabel.setBounds(0, 30, 85, 20);
		tab0.add(unitLengthLabel);

		unitLenComboBox = new JComboBox();
		unitLenComboBox.setModel(new DefaultComboBoxModel(new String[] {"16 x 16 pixels", "10 x 10 pixels"}));
		unitLenComboBox.setBounds(90, 30, 120, 20);
		tab0.add(unitLenComboBox);

		final JButton applyDimButton_2 = new JButton();
		applyDimButton_2.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				if(bigRadioButton.isSelected()) {
					if(unitLenComboBox.getSelectedIndex() == 0) {
						gridPanel.setPreferredSize(new Dimension(8040, 8030));
					} else if(unitLenComboBox.getSelectedIndex() == 1) {
						gridPanel.setPreferredSize(new Dimension(5030, 5020));
					}
					gridPanel.setSpaceDimension(500);
				} else {
					if(unitLenComboBox.getSelectedIndex() == 0) {
						gridPanel.setPreferredSize(new Dimension(830, 820));
					} else if(unitLenComboBox.getSelectedIndex() == 1) {
						gridPanel.setPreferredSize(new Dimension(500, 500));
					}
					gridPanel.setSpaceDimension(50);
				}
				String tmp = unitLenComboBox.getSelectedItem().toString().substring(0,2);
				if(tmp.charAt(1) == ' ') {
					tmp = tmp.substring(0, 1);
				}
				gridPanel.setUnitLen(Integer.parseInt(tmp));
				
				//repaint the grid panel
				gridPanel.setCommand(JGridPanel.PAINT_COMMAND_DRAWGRID);
				gridPanel.repaint();	
				
				//repaint canvas
				canvasPanel.updateUI();
				canvasPanel.revalidate();
				canvasPanel.repaint();
			}
		});
		applyDimButton_2.setText("Apply");
		applyDimButton_2.setBounds(270, 30, 75, 20);
		tab0.add(applyDimButton_2);

		final JPanel tab1 = new JPanel();
		tab1.setLayout(null);
		tabbedPane.addTab("Resources Settings", null, tab1, null);

		final JLabel resNb1Label = new JLabel();
		resNb1Label.setBounds(0, 4, 135, 20);
		resNb1Label.setText("Nb. Res Type1 (Red) :");
		tab1.add(resNb1Label);

		resNb1textField = new JTextField();
		resNb1textField.setText("0");
		resNb1textField.setBounds(140, 4, 50, 20);
		resNb1textField.setPreferredSize(new Dimension(50, 25));
		tab1.add(resNb1textField);

		final JLabel resNb2Label = new JLabel();
		resNb2Label.setText("Nb. Res Type2 (Blue) :");
		resNb2Label.setBounds(0, 30, 135, 20);
		tab1.add(resNb2Label);

		resNb2textField = new JTextField();
		resNb2textField.setText("0");
		resNb2textField.setBounds(140, 30, 50, 20);
		tab1.add(resNb2textField);

		final JLabel resNb3Label = new JLabel();
		resNb3Label.setText("Nb. Res Type3 (Green) :");
		resNb3Label.setBounds(230, 4, 145, 20);
		tab1.add(resNb3Label);

		resNb3textField = new JTextField();
		resNb3textField.setText("0");
		resNb3textField.setBounds(380, 4, 50, 20);
		tab1.add(resNb3textField);

		final JButton initButton = new JButton();
		initButton.setMargin(new Insets(2, 6, 2, 6));
		initButton.setToolTipText("Initialize the resources at the random position.");
		initButton.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				//TODO init the res. and the ants
				int nbRes_type1 = Integer.parseInt(resNb1textField.getText());
				int nbRes_type2 = Integer.parseInt(resNb2textField.getText());
				int nbRes_type3 = Integer.parseInt(resNb3textField.getText());
				
				SpaceUtilities.initResourcesAtRandom(nbRes_type1, nbRes_type2, nbRes_type3, gridPanel.getSpace());
				
				//repaint the grid panel
				gridPanel.setCommand(JGridPanel.PAINT_COMMAND_INITRES);
				gridPanel.repaint();	
				
				//repaint canvas
				canvasPanel.updateUI();
				canvasPanel.revalidate();
				canvasPanel.repaint();
			}
		});
		
		initButton.setText("Init. Resources");
		initButton.setBounds(270, 30, 120, 20);
		tab1.add(initButton);

		final JPanel tab2 = new JPanel();
		tab2.setLayout(null);
		tabbedPane.addTab("Ants Settings", null, tab2, "Set the nomber of the ants and their start points.");

		final JLabel addAntsLabel = new JLabel();
		addAntsLabel.setBounds(0, 4, 120, 20);
		addAntsLabel.setText("Add a Random  Ant : ");
		tab2.add(addAntsLabel);

		antXTextField = new JTextField();
		antXTextField.setToolTipText("Coordinate X from 0 to 29 (for dim 30 x 30) or to 99 (for dim 100 x 100)");
		antXTextField.setText("0");
		antXTextField.setBounds(20, 30, 25, 20);
		tab2.add(antXTextField);

		final JLabel antXLabel = new JLabel();
		antXLabel.setText("X : ");
		antXLabel.setBounds(0, 30, 20, 20);
		tab2.add(antXLabel);

		final JLabel antYLabel = new JLabel();
		antYLabel.setText("Y : ");
		antYLabel.setBounds(60, 30, 20, 20);
		tab2.add(antYLabel);

		antYTextField = new JTextField();
		antYTextField.setToolTipText("Coordinate Y from 0 to 29 (for dim 30 x 30) or to 99 (for dim 100 x 100)");
		antYTextField.setText("0");
		antYTextField.setBounds(85, 30, 25, 20);
		tab2.add(antYTextField);

		//declaration moved here
		final JCheckBox activatePheromoneModeCheckBox = new JCheckBox();
		
		final JButton addAntButton = new JButton();
		addAntButton.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				Point p = new Point(Integer.parseInt(antXTextField.getText()), Integer.parseInt(antYTextField.getText()));
				SpaceUtilities.initSpecifiedAnt(gridPanel.getSpace(), p, activatePheromoneModeCheckBox.isSelected());
				
				//repaint the grid panel
				gridPanel.setCommand(JGridPanel.PAINT_COMMAND_INITANTS);
				gridPanel.repaint();	
				
				//repaint canvas
				/*canvasPanel.updateUI();
				canvasPanel.revalidate();
				canvasPanel.repaint();*/
			}
		});
		addAntButton.setMargin(new Insets(2, 6, 2, 6));
		addAntButton.setToolTipText("Add an ant with a start point (X, Y)");
		addAntButton.setText("Add");
		addAntButton.setBounds(130, 30, 60, 20);
		tab2.add(addAntButton);

		final JButton addRandomAntButton = new JButton();
		addRandomAntButton.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				//add a random ant
				SpaceUtilities.initRandomAnt(gridPanel.getSpace(), activatePheromoneModeCheckBox.isSelected());
				
				//repaint the grid panel
				gridPanel.setCommand(JGridPanel.PAINT_COMMAND_INITANTS);
				gridPanel.repaint();	
				
				//repaint canvas
				/*canvasPanel.updateUI();
				canvasPanel.revalidate();
				canvasPanel.repaint();*/
			}
		});
		addRandomAntButton.setToolTipText("Add an ant with a random start point.");
		addRandomAntButton.setMargin(new Insets(2, 4, 2, 4));
		addRandomAntButton.setText("Add");
		addRandomAntButton.setBounds(130, 4, 60, 20);
		tab2.add(addRandomAntButton);

		//the declaration is moved here
		final JCheckBox showPheromoneCheckBox = new JCheckBox();
		showPheromoneCheckBox.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent e) {
				if(showPheromoneCheckBox.isSelected()) {
					//gridPanel.setShowPheromone(true);
				} else {
					//gridPanel.setShowPheromone(false);
				}
			}
		});
		
		//make the declaration in advance
		//final JCheckBox activatePheromoneModeCheckBox = new JCheckBox();
		activatePheromoneModeCheckBox.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				if(activatePheromoneModeCheckBox.isSelected()) {
					//gridPanel.setActivatePheromone(true);
					showPheromoneCheckBox.setEnabled(true);
					
					//set activate pheromone true in each ant
					Ant ant = null;
					for(int i = 0; i < gridPanel.getSpace().getListAnts().size(); i++) {
						ant = (Ant)gridPanel.getSpace().getListAnts().get(i);
						ant.setPheromoneActivated(true);
					}
				} else {
					//gridPanel.setActivatePheromone(false);
					showPheromoneCheckBox.setEnabled(false);
					
					Ant ant = null;
					for(int i = 0; i < gridPanel.getSpace().getListAnts().size(); i++) {
						ant = (Ant)gridPanel.getSpace().getListAnts().get(i);
						ant.setPheromoneActivated(false);
					}
				}
			}
		});
		activatePheromoneModeCheckBox.setToolTipText("Choose that an ant leaves the pheromone or not.");
		activatePheromoneModeCheckBox.setMargin(new Insets(2, 0, 2, 2));
		activatePheromoneModeCheckBox.setText("Activate Pheromone");
		activatePheromoneModeCheckBox.setBounds(340, 30, 140, 20);
		tab2.add(activatePheromoneModeCheckBox);

		final JLabel antDelayLabel = new JLabel();
		antDelayLabel.setText("Step Delay (ms) : ");
		antDelayLabel.setBounds(220, 4, 110, 20);
		tab2.add(antDelayLabel);

		final JButton setDelayButton = new JButton();
		setDelayButton.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				setDelay(Integer.parseInt(antDelayTextField.getText()));
			}
		});
		setDelayButton.setToolTipText("Remove an added ant from the list.");
		setDelayButton.setMargin(new Insets(2, 6, 2, 6));
		setDelayButton.setText("Set Delay");
		setDelayButton.setBounds(400, 4, 80, 20);
		tab2.add(setDelayButton);

		antDelayTextField = new JTextField();
		antDelayTextField.setBounds(330, 4, 50, 20);
		tab2.add(antDelayTextField);

		final JCheckBox showtraceCheckBox = new JCheckBox();
		showtraceCheckBox.setSelected(true);
		showtraceCheckBox.setText("Show Ant Trace");
		showtraceCheckBox.setBounds(220, 30, 120, 20);
		tab2.add(showtraceCheckBox);

		//make the declaration in advance
		//final JCheckBox showPheromoneCheckBox = new JCheckBox();
		showPheromoneCheckBox.setMargin(new Insets(2, 0, 2, 2));
		showPheromoneCheckBox.setText("Show Pheromone");
		showPheromoneCheckBox.setBounds(480, 30, 130, 20);
		tab2.add(showPheromoneCheckBox);
		showPheromoneCheckBox.setEnabled(false);
		
		final JPanel tab3 = new JPanel();
		tab3.setLayout(null);
		tabbedPane.addTab("Attraction Zone", null, tab3, null);

		//move the declaration in advance
		//final JCheckBox attrZoneCheckBox = new JCheckBox();
		attrZoneCheckBox.setText("Enable Attraction Zone");
		attrZoneCheckBox.setBounds(0, 4, 170, 20);
		tab3.add(attrZoneCheckBox);

		final JLabel orientationLabel = new JLabel();
		orientationLabel.setText("Orientation :");
		orientationLabel.setBounds(180, 4, 80, 20);
		tab3.add(orientationLabel);

		orientationComboBox = new JComboBox();
		orientationComboBox.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent e) {
				/*System.out.println("Orientation changed to :" + orientationComboBox.getSelectedItem() + 
						" / " + orientationComboBox.getSelectedIndex());*/
				gridPanel.setOrientation((String)orientationComboBox.getSelectedItem());
			}
		});
		orientationComboBox.setModel(new DefaultComboBoxModel(new String[] {"North", "South", "East", "West", "North-East", "North-West", "South-East", "South-West"}));
		orientationComboBox.setBounds(265, 4, 120, 20);
		tab3.add(orientationComboBox);

		final JButton applyAttrSettingsButton = new JButton();
		applyAttrSettingsButton.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				gridPanel.setActivateAttractionZone(true);
			}
		});
		applyAttrSettingsButton.setText("Apply");
		applyAttrSettingsButton.setBounds(400, 4, 70, 20);
		tab3.add(applyAttrSettingsButton);

		final JCheckBox showAttrAreaCheckBox = new JCheckBox();
		showAttrAreaCheckBox.setSelected(true);
		showAttrAreaCheckBox.setText("Show Acctractio Area");
		showAttrAreaCheckBox.setBounds(0, 30, 170, 20);
		tab3.add(showAttrAreaCheckBox);

		//final JCheckBox ajustAttrAreaCheckBox = new JCheckBox();
		ajustAttrAreaCheckBox.setToolTipText("Click the cell to add it into the attraction zone.");
		ajustAttrAreaCheckBox.setMargin(new Insets(2, 0, 2, 2));
		ajustAttrAreaCheckBox.setText("Ajust Attraction Area");
		ajustAttrAreaCheckBox.setBounds(180, 30, 150, 20);
		tab3.add(ajustAttrAreaCheckBox);

		final JButton cancelAttrSettingsButton = new JButton();
		cancelAttrSettingsButton.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				gridPanel.setActivateAttractionZone(false);
				gridPanel.getAttractionPoints().clear();
				gridPanel.setOrientation("");
				orientationComboBox.setSelectedIndex(0);
				gridPanel.repaint();
			}
		});
		cancelAttrSettingsButton.setMargin(new Insets(2, 12, 2, 12));
		cancelAttrSettingsButton.setText("Cancel");
		cancelAttrSettingsButton.setBounds(400, 30, 70, 20);
		tab3.add(cancelAttrSettingsButton);

		//enable attraction zone checkbox listener
		attrZoneCheckBox.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				//TODO display the attraction zone
				if(attrZoneCheckBox.isSelected()) {
					orientationComboBox.setEnabled(true);
					
					//set the show checkbox always disabled
					//showAttrAreaCheckBox.setEnabled(true);
					
					ajustAttrAreaCheckBox.setEnabled(true);
					applyAttrSettingsButton.setEnabled(true);
					cancelAttrSettingsButton.setEnabled(true);
					orientationLabel.setEnabled(true);
				} else {
					orientationComboBox.setEnabled(false);
					showAttrAreaCheckBox.setEnabled(false);
					ajustAttrAreaCheckBox.setEnabled(false);
					applyAttrSettingsButton.setEnabled(false);
					cancelAttrSettingsButton.setEnabled(false);
					orientationLabel.setEnabled(false);
				}
			}
		});
		//set false as initialization
		orientationComboBox.setEnabled(false);
		showAttrAreaCheckBox.setEnabled(false);
		ajustAttrAreaCheckBox.setEnabled(false);
		applyAttrSettingsButton.setEnabled(false);
		cancelAttrSettingsButton.setEnabled(false);
		orientationLabel.setEnabled(false);
		
		//button design and behavior
		final JPanel jButtonPanel = new JPanel();
		getContentPane().add(jButtonPanel, BorderLayout.SOUTH);

		final JButton clearButton = new JButton();
		clearButton.setToolTipText("This action will permanently remove all the objects including the ants and the resources.");
		clearButton.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				//clear the grid panel
				gridPanel.setCommand(JGridPanel.PAINT_COMMAND_CLEAR);
				gridPanel.repaint();	
			}
		});
		clearButton.setText("Clear");
		jButtonPanel.add(clearButton);

		
		
		actionComboBox = new JComboBox();
		actionComboBox.setToolTipText("Select an action to run...");
		actionComboBox.setPreferredSize(new Dimension(220, 25));
		actionComboBox.setMaximumSize(new Dimension(0, 0));
		actionComboBox.setMaximumRowCount(10);
		actionComboBox.setModel(new DefaultComboBoxModel(new String[] {"( Select an action to run... )", "Random walk 1000 steps.", "Random walk with a histogram.", "Collect and pile up the resources.", "Action 04"}));
		jButtonPanel.add(actionComboBox);
		
		//ComboBoxRender for actionComboBox
		final String[] tooltips = new String[] {
			"Select a following action in the list to run",
			"An (some) ant(s) walk(s) randomly at 1000 steps.",
			"An (some) ant(s) walk(s) randomly at 1000 steps and generate according to the resource type the histograms which represent the the steps moved between 2 discoveries of the resources of same type.",
			"An (some) ant(s) walk(s) randomly infinitely to cellect and pile up resourcs according to the resource type until STOP clicked.",
			"Action currently not defined.",
		};
		class actionComboBoxRenderer extends BasicComboBoxRenderer {
			private static final long serialVersionUID = 1L;

			public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
				if(isSelected) {
					setBackground(list.getSelectionBackground());
					setForeground(list.getSelectionForeground());
					if (-1 < index) {
						list.setToolTipText(tooltips[index]);
					}
				} else {
					setBackground(list.getBackground());
					setForeground(list.getForeground());
				}
				setFont(list.getFont());
				setText((value == null) ? "" : value.toString());
				return this;
			}
		}
		actionComboBox.setRenderer(new actionComboBoxRenderer());
		
		final JButton runButton = new JButton();
		runButton.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				
				gridPanel.setCommand(JGridPanel.PAINT_COMMAND_ACTION);
				gridPanel.setActionIndex(actionComboBox.getSelectedIndex());
				
				//disable the buttons
				gridPanel.setShowTrace(showtraceCheckBox.isSelected());
				gridPanel.setActivatePheromone(activatePheromoneModeCheckBox.isSelected());
				gridPanel.setShowPheromone(showPheromoneCheckBox.isSelected());
				showtraceCheckBox.setEnabled(false);
				activatePheromoneModeCheckBox.setEnabled(false);
				showPheromoneCheckBox.setEnabled(false);
				orientationComboBox.setEnabled(false);
				showAttrAreaCheckBox.setEnabled(false);
				ajustAttrAreaCheckBox.setEnabled(false);
				applyAttrSettingsButton.setEnabled(false);
				cancelAttrSettingsButton.setEnabled(false);
				orientationLabel.setEnabled(false);
				attrZoneCheckBox.setEnabled(false);
				
				setStopSignal(false);
				gridPanel.initTraceColor();
				
				//calcul thread
				startRunner();
			}
		});
		runButton.setText("Run");
		jButtonPanel.add(runButton);

		final JButton stopButton = new JButton();
		stopButton.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0) {
				setStopSignal(true);
				
				//re activate the buttons
				showtraceCheckBox.setEnabled(true);
				activatePheromoneModeCheckBox.setEnabled(true);
				if(activatePheromoneModeCheckBox.isSelected())
					showPheromoneCheckBox.setEnabled(true);
				orientationComboBox.setEnabled(true);
				ajustAttrAreaCheckBox.setEnabled(true);
				applyAttrSettingsButton.setEnabled(true);
				cancelAttrSettingsButton.setEnabled(true);
				orientationLabel.setEnabled(true);;
				attrZoneCheckBox.setEnabled(true);
			}
		});
		stopButton.setText("Stop");
		jButtonPanel.add(stopButton);
		//
	}

	/*private void enableAttrComponents() {
		orientationComboBox.setEnabled(true);
		ajustAttrAreaCheckBox.setEnabled(true);
		applyAttrSettingsButton.setEnabled(true);
		cancelAttrSettingsButton.setEnabled(true);
		orientationLabel.setEnabled(true);;
	}
	
	private void disableAttrComponents() {
		orientationComboBox.setEnabled(false);
		showAttrAreaCheckBox.setEnabled(false);
		ajustAttrAreaCheckBox.setEnabled(false);
		applyAttrSettingsButton.setEnabled(false);
		cancelAttrSettingsButton.setEnabled(false);
		orientationLabel.setEnabled(false);
	}*/
	
	private void antRandomWalk(boolean animationOn) {
		ArrayList<Ant> antList = canvasPanel.getGridPanel().getSpace().getListAnts();
		canvasPanel.getGridPanel().getSubActionHelper().initializeAntsTrace(antList.size());
		
		//definition of inner -- task
		Thread task = new Thread() {
			public void run() {
				canvasPanel.getGridPanel().repaint();
				try {
					Thread.sleep(getDelay());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
		     }
		}; 
		
		for(int i= 0; i < 1000 && !getStopSignal(); i++) {
			try {
				for(int j = 0; j < antList.size(); j++) {
					Ant ant = antList.get(j);
					canvasPanel.getGridPanel().getSubActionHelper().setActiveAntNb(j);
					canvasPanel.getGridPanel().getSubActionHelper().setTraceList(j, ant.getHistMoves());
					ant.move();
					
					SwingUtilities.invokeAndWait(task);
				}
			} catch (Exception e) {
				System.err.println("Ant move failed.");
				e.printStackTrace();
			}
		}
	}
	
	private void pileUpResources(boolean animationOn) {
		Space2D space = canvasPanel.getGridPanel().getSpace();
		ArrayList<Ant> antList = space.getListAnts();
		canvasPanel.getGridPanel().getSubActionHelper().initializeAntsTrace(antList.size());
		
		Thread task = new Thread() {
			public void run() {
				canvasPanel.getGridPanel().repaint();
				try {
					Thread.sleep(getDelay());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
		     }
		}; 
		
		while(true && !getStopSignal()) {
			try {
				for(int j = 0; j < antList.size(); j++) {
					Ant ant = antList.get(j);
					//if an ant has not taken any resources, then take a resource
					Cell cell = space.getCell(ant.getLocation());
	
					if(ant.getResource() == null) {
						//test if current position has resource
						//the algorithme is descripted in the method ant.putDownResource();
						if(cell.containsResource())
							ant.takeResource();
					} else {
						//in order to put down the carried resource, the current position should be free
						//the algorithme is descripted in the method ant.putDownResource(); 
						if(space.getCell(ant.getLocation()).getResource() == null)
							ant.putDownResource();
					}
					
					canvasPanel.getGridPanel().getSubActionHelper().setActiveAntNb(j);
					canvasPanel.getGridPanel().getSubActionHelper().setTraceList(j, ant.getHistMoves());

					SwingUtilities.invokeAndWait(task);
					ant.move();
				}
			} catch (Exception e) {
				System.err.println("Ant move failed.");
				e.printStackTrace();
			}
		}
		
		
	}
	
	public void run() {
		switch(actionComboBox.getSelectedIndex()) {
		case 1:
			//random walks at 1000 steps
			antRandomWalk(true);
			break;
		case 2:
			//random walks and generate a histogram
			antRandomWalk(true);
			//display the histograms after the animation
			Histogram[] histoFrame = new Histogram[canvasPanel.getGridPanel().getSpace().getCptAnts()]; 
			for(int i = 0; i < canvasPanel.getGridPanel().getSpace().getCptAnts(); i++) {
				histoFrame[i] = new Histogram(canvasPanel.getGridPanel().getSpace(), i, canvasPanel.getGridPanel().getAntTraceColor(i));
				histoFrame[i].setTitle("Resources Histograms for Ant " + Integer.toString(i));
				histoFrame[i].setVisible(true);
			}
			break;
		case 3:
			//collect the resources and make a stack
			pileUpResources(true);
		default:
			break;
		}
	}
	
	public void startRunner() {
		if(runner == null || !runner.isAlive())
			runner = new Thread(this);
		runner.start();
	}
	
	public void stopRunner() {
		runner = null;;
	}

	public int getDelay() {
		return delay;
	}

	public void setDelay(int delay) {
		this.delay = delay;
	}
	
	public boolean getStopSignal() {
		return stopSignal;
	}

	public void setStopSignal(boolean stopSignal) {
		this.stopSignal = stopSignal;
	}
	
}

class JCanvasPanel extends JScrollPane {
	private static final long serialVersionUID = -5727528674248665693L;
	
	private JGridPanel gridPanel;
	
	public JCanvasPanel() {
		super();
	}
	
	public JGridPanel getGridPanel() {
		return gridPanel;
	}

	public void setGridPanel(JGridPanel gridPanel) {
		this.gridPanel = gridPanel;
	}

}

class JGridPanel extends JPanel {  
	private static final long serialVersionUID = 3356506503283729316L;
	
	public static final int PAINT_COMMAND_CLEAR = 0;
	public static final int PAINT_COMMAND_DRAWGRID = 1;
	public static final int PAINT_COMMAND_INITRES = 2;
	public static final int PAINT_COMMAND_INITANTS = 3;
	public static final int PAINT_COMMAND_ACTION = 4;
	
	private int command;
	private int actionIndex;
	private boolean activatePheromone;
	private boolean showPheromone;
	private boolean showTrace;
	
	private Space2D space;
	private int spaceDim;
	private int unitLen;
	private Color[] cs;
	private SubActionHelper subActionHelper;
	private boolean activateAttractionZone;
	private HashMap<String, Point> attractionPoints;
	private String orientation;
	
	public JGridPanel() {
		super();
		//space = SpaceUtilities.initSpace(new Space2D(spaceDim, spaceDim));
		command = -1;
		actionIndex = -1;
		subActionHelper = new SubActionHelper();
		attractionPoints = new HashMap<String, Point>();
		orientation = "";
	}
	
	/** 
	 * Override the tooltip method
	 */
	@Override 
	public String getToolTipText(MouseEvent event) {
		if(space != null) {
			Point p = SpaceUtilities.transformCoordinate(event.getPoint(), unitLen);
			
			StringBuffer tip = new StringBuffer();
			tip.append("<html>");
			tip.append("Coordinate : ");
			tip.append(p.toString());
			tip.append("<br>");
			if(activatePheromone) {
				tip.append("Pheromone : ");
				tip.append(space.getCell(p).getPheromone());
				tip.append("<br>");
			}
			if(activateAttractionZone) {
				tip.append("Attraction Point : ");
				if(attractionPoints.get(p.getX()+","+p.getY()) == null) {
					tip.append("No.");
				} else {
					tip.append("Yes, Orientation : ");
					tip.append(orientation);
				}
			}
			tip.append("</html>");
			return tip.toString();
		} else {
			return "Space not yet initialized.";
		}
    }

	@Override 
	public void paint(Graphics g) {
		super.paint(g);
		g.setColor(Color.GRAY);	
		this.setBackground(Color.WHITE);
		
		if(command == JGridPanel.PAINT_COMMAND_CLEAR) {
			//clear all
			space.clear();
			g.setColor(Color.WHITE);
			g.clearRect(0, 0, getWidth(), getHeight());
		}
		
		if(command >= JGridPanel.PAINT_COMMAND_DRAWGRID) {
			//draw grid
			for(int i = 0; i < spaceDim+1; i++) {
				//draw horizontal lines
				g.setColor(Color.LIGHT_GRAY);
				((Graphics2D)g).setStroke(new BasicStroke(1));
				g.drawLine(0, i*unitLen, spaceDim*unitLen, i*unitLen);
				//draw vertical lines
				g.drawLine(i*unitLen, 0, i*unitLen, spaceDim*unitLen);
				
				if(i < spaceDim) {
					g.setColor(Color.BLACK);
					((Graphics2D)g).setStroke(new BasicStroke(2));
					((Graphics2D)g).setFont(new Font("Times New Roman", Font.BOLD, 10));
					//x scale
					g.drawString(Integer.toString(i), (spaceDim+1)*unitLen, (int)((i+0.8)*unitLen));
					//y scale
					g.drawString(Integer.toString(i), (int)((i+0.3)*unitLen), (spaceDim+1)*unitLen);
					
				}	
				
				//color the attraction points
				SpaceUtilities.colorAttractionPoints(g, unitLen, attractionPoints);
				
				//color the pheromone effect
				if(activatePheromone && showPheromone) {
					System.out.println("Color the pheromone.");
					SpaceUtilities.colorPheromonePoints(g, unitLen, space);
				}
			}
		}
		
		if(command >= JGridPanel.PAINT_COMMAND_INITRES) {
			//draw resources
			for(int i = 0; i < space.getListRes().size(); i++) {
				SpaceUtilities.colorResource(g, (Resource)space.getListRes().get(i), this.unitLen);
			}
		}
		
		if(command == JGridPanel.PAINT_COMMAND_INITANTS) {
			//draw ants
			for(int i = 0; i < space.getListAnts().size(); i++) {
				SpaceUtilities.drawAnt(g, ((Ant)space.getListAnts().get(i)).getLocation(), this.unitLen);
			}
		}
		
		//remove an ant
		//SpaceUtilities.removeAnt(g, (Ant)space.getListAnts().get(1), this.unitLen);
		
		//paint action
		if(command >= JGridPanel.PAINT_COMMAND_ACTION) {
			switch (actionIndex) {
				case 1 : //random walk at 1000 steps	
					//SpaceUtilities.drawTrace(g, getSubActionHelper().getP1(), getSubActionHelper().getP2(), unitLen);
					drawAntMoveAnimation(g);
					break;
				case 2 : //random walk and generate a histogram
					//same as the case above
					drawAntMoveAnimation(g);
					//calculate and display the histogram after the execution of this repaint method
					break;
				case 3 :
					drawAntMoveAnimation(g);
					break;
			}
		}
	}

	public void drawAntMoveAnimation(Graphics g) {
		int activeAntNb = this.getSubActionHelper().getActiveAntNb();
				
		//for each step		
		Vector<Point> traceList = null;
		for(int k = 1; k < getSubActionHelper().getTraceList(activeAntNb).size(); k++) {
			Point p0 = null;
			Point p1 = null;
			Point p2 = null;
			// for each ant
			for(int i = 0; i < space.getCptAnts(); i++) {
				traceList = getSubActionHelper().getTraceList(i);
				if(k < traceList.size()) {
					p1 = traceList.get(k-1);
					p2 = traceList.get(k);
					
					SpaceUtilities.removeAnt(g, space, p1, unitLen);
					if(isShowTrace()) {
						for(int j = 1; j < k; j++) {
							for(int m = 0; m < getSubActionHelper().getAntsTrace().size(); m++) {
								if(getSubActionHelper().getTraceList(m).size() > j)
									p0 = getSubActionHelper().getTraceList(m).get(j);
								//if has already passed this point 
								if(p1.equals(p0)) {
									SpaceUtilities.drawTrace(g, p0, getSubActionHelper().getTraceList(m).get(j-1), unitLen, cs[m]);
									SpaceUtilities.drawTrace(g, p0, getSubActionHelper().getTraceList(m).get(j+1), unitLen, cs[m]);
								}
							}
						}
						
						SpaceUtilities.drawTrace(g, p1, p2, unitLen, cs[i]);
					}
					SpaceUtilities.drawAnt(g, p2, unitLen);
					p1 = p2;
				}
				if(traceList.size() > 0) {
					SpaceUtilities.markPoint((Graphics2D)g, traceList.get(0), Color.DARK_GRAY, unitLen);
				}
			}
		}
		
	}
	
	public void initTraceColor() {
		//color settings
		this.cs = new Color[space.getListAnts().size()];
		this.cs = SpaceUtilities.initColorArray(cs);
	}
	
	public Color getAntTraceColor(int antNb) {
		//color settings
		return cs[antNb];
	}
	
	public int getActionIndex() {
		return actionIndex;
	}

	public void setActionIndex(int actionIndex) {
		this.actionIndex = actionIndex;
	}

	public int getSpaceDimension() {
		return spaceDim;
	}

	public void setSpaceDimension(int spaceDim) {
		this.spaceDim = spaceDim;
		//each time a change of the grid will cause the creation of a new space2D object
		space = new Space2D(spaceDim, spaceDim);
	}

	public int getUnitLen() {
		return unitLen;
	}

	public void setUnitLen(int unitLen) {
		this.unitLen = unitLen;
	}

	public int getCommand() {
		return command;
	}

	public void setCommand(int command) {
		this.command = command;
	}
	
	public Space2D getSpace() {
		return space;
	}

	public boolean isActivatePheromone() {
		return activatePheromone;
	}

	public void setActivatePheromone(boolean activatePheromone) {
		this.activatePheromone = activatePheromone;
	}
	
	public SubActionHelper getSubActionHelper() {
		return this.subActionHelper;
	}

	public boolean isShowTrace() {
		return showTrace;
	}

	public void setShowTrace(boolean showTrace) {
		this.showTrace = showTrace;
	}

	public HashMap<String, Point> getAttractionPoints() {
		return attractionPoints;
	}

	public void setAttractionPoints(HashMap<String, Point> attractionPoints) {
		this.attractionPoints = attractionPoints;
	}
	
	public void addAttractionPoint(Point attractionPoint) {
		int x = attractionPoint.getX();
		int y = attractionPoint.getY();
		if(attractionPoints.get(x + "," + y) == null) {
			this.attractionPoints.put(x + "," + y, attractionPoint);
		}
	}
	
	public Point getAttractionPoint(String coordinate) {
		return this.attractionPoints.get(coordinate);
	}

	public String getOrientation() {
		return orientation;
	}

	public void setOrientation(String orientation) {
		this.orientation = orientation;
	}

	public boolean isActivateAttractionZone() {
		return activateAttractionZone;
	}

	public void setActivateAttractionZone(boolean activateAttractionZone) {
		this.activateAttractionZone = activateAttractionZone;
	}

	public boolean isShowPheromone() {
		return showPheromone;
	}

	public void setShowPheromone(boolean showPheromone) {
		this.showPheromone = showPheromone;
	}
}

class SubActionHelper {
	private Vector<Point> traceList;
	private ArrayList<Vector<Point>> antsTrace;
	private int activeAntNb;
	
	public SubActionHelper() {
		traceList = new Vector<Point>(); 
		activeAntNb = 0;
	}

	public Vector<Point> getTraceList() {
		return traceList;
	}

	public void setTraceList(Vector<Point> traceList) {
		this.traceList = traceList;
	}

	public void addArriedPoint(Point p) {
		traceList.add(p);
	}

	public ArrayList<Vector<Point>> getAntsTrace() {
		return antsTrace;
	}
	
	public void initializeAntsTrace(int nbAnts) {
		antsTrace = new ArrayList<Vector<Point>>();
		for(int i = 0; i < nbAnts; i++) {
			antsTrace.add(new Vector<Point>());
		}
	}
	
	public void addArriedPoint(int antNb, Point p) {
		antsTrace.get(antNb).add(p);
	}
	
	public void setTraceList(int antNb, Vector<Point> traceList) {
		antsTrace.set(antNb, traceList);
	}

	public Vector<Point> getTraceList(int antNb) {
		return antsTrace.get(antNb);
	}
	
	public int getActiveAntNb() {
		return activeAntNb;
	}

	public void setActiveAntNb(int activeAntNb) {
		this.activeAntNb = activeAntNb;
	}
}